package com.flyme.common.security;

import com.flyme.common.security.filter.AbstractDispatchFilter;
import com.flyme.common.security.filter.AbstractAccessControlFilter;
import com.flyme.common.security.filter.AuthenticationFilter;
import com.flyme.common.security.filter.FilterChainManager;
import com.flyme.common.security.filter.FilterChainResolver;
import com.flyme.common.security.filter.Nameable;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.util.CollectionUtils;

import javax.servlet.Filter;
import java.util.Map;

/**
 * Created by xinxingegeya on 2016/11/30.
 */
public class FilterFactoryBean implements FactoryBean {

    private static final Logger log = LoggerFactory.getLogger(AbstractAccessControlFilter.class);

    private Map<String, Filter> filters;
    private Map<String, String> filterChainDefinitions;
    private AbstractDispatchFilter instance;
    private String loginUrl;
    private String successUrl;

    public String getLoginUrl() {
        return loginUrl;
    }

    public void setLoginUrl(String loginUrl) {
        this.loginUrl = loginUrl;
    }

    public String getSuccessUrl() {
        return successUrl;
    }

    public void setSuccessUrl(String successUrl) {
        this.successUrl = successUrl;
    }

    private void applyLoginUrlIfNecessary(Filter filter) {
        String loginUrl = getLoginUrl();
        if (StringUtils.isNotBlank(loginUrl) && (filter instanceof AuthenticationFilter)) {
            AuthenticationFilter authcFilter = (AuthenticationFilter) filter;
            //only apply the login url if they haven't explicitly configured one already:
            String existingLoginUrl = authcFilter.getLoginUrl();
            if (AuthenticationFilter.DEFAULT_LOGIN_URL.equals(existingLoginUrl)) {
                authcFilter.setLoginUrl(loginUrl);
            }
        }
    }

    private void applySuccessUrlIfNecessary(Filter filter) {
        String successUrl = getSuccessUrl();
        if (StringUtils.isNotBlank(successUrl) && (filter instanceof AuthenticationFilter)) {
            AuthenticationFilter authcFilter = (AuthenticationFilter) filter;
            //only apply the successUrl if they haven't explicitly configured one already:
            String existingSuccessUrl = authcFilter.getSuccessUrl();
            if (AuthenticationFilter.DEFAULT_SUCCESS_URL.equals(existingSuccessUrl)) {
                authcFilter.setSuccessUrl(successUrl);
            }
        }
    }

    @Override
    public Object getObject() throws Exception {
        if (instance == null) {
            instance = createInstance();
        }
        return instance;
    }

    protected FilterChainManager createFilterChainManager() {
        FilterChainManager manager = new FilterChainManager();
        Map<String, Filter> filters = getFilters();
        if (!CollectionUtils.isEmpty(filters)) {
            for (Map.Entry<String, Filter> entry : filters.entrySet()) {
                String name = entry.getKey();       //filter name
                Filter filter = entry.getValue();   //filter

                applyLoginUrlIfNecessary(filter);
                applySuccessUrlIfNecessary(filter);

                if (filter instanceof Nameable) {
                    ((Nameable) filter).setName(name);
                }
                manager.addFilter(name, filter);
            }
        }
        //build up the chains:
        Map<String, String> chains = getFilterChainDefinitions();
        if (!CollectionUtils.isEmpty(chains)) {
            for (Map.Entry<String, String> entry : chains.entrySet()) {
                String url = entry.getKey();
                String chainDefinition = entry.getValue();
                manager.createChain(url, chainDefinition);
            }
        }

        return manager;
    }

    private CommonFilter createInstance() {
        FilterChainManager manager = createFilterChainManager();
        FilterChainResolver chainResolver = new FilterChainResolver();
        chainResolver.setFilterChainManager(manager);
        return new CommonFilter(chainResolver);
    }

    @Override
    public Class<?> getObjectType() {
        return FilterFactoryBean.class;
    }

    @Override
    public boolean isSingleton() {
        return true;
    }

    public Map<String, Filter> getFilters() {
        return filters;
    }

    public void setFilters(Map<String, Filter> filters) {
        this.filters = filters;
    }

    public Map<String, String> getFilterChainDefinitions() {
        return filterChainDefinitions;
    }

    public void setFilterChainDefinitions(Map<String, String> filterChainDefinitions) {
        this.filterChainDefinitions = filterChainDefinitions;
    }

    private static final class CommonFilter extends AbstractDispatchFilter {
        public CommonFilter(FilterChainResolver filterChainResolver) {
            super();
            if (filterChainResolver != null) {
                setFilterChainResolver(filterChainResolver);
            }
        }
    }
}
